package net.alche.sdd.db.sqlite;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.alche.al.util.AC;
import net.alche.al.util.Far;
import net.alche.al.util.M;
import net.alche.sdd.fatis.SddDataSource;
import net.alche.sdd.fatis.SddDataSourceConnection;

public class DbSqliteSource extends SddDataSource {
  public static final Logger logger = LoggerFactory.getLogger(DbSqliteSource.class);
  List<SddDataSourceConnection> links = new ArrayList<>(); // 被使用链接
  // List<SddDataSourceConnection> idlelLinks = new LinkedList<>(); // 空闲链接
  // public int maxCount = 20;
  // public int minCount = 3;
  // public int maxWaitMs = 1000 * 60; // 空闲最大等待
  // public int peakWaitMs = 1000 * 10; // 超过最小链接数的，峰值链接保留时间
  private Timer timerFree; // 空闲链接检查器
  private long timerFreePeriod = 1000 * 10; // 单位：毫秒
  // public boolean logPoolInfo = false;
  String dbPath = ""; // 存放路径
  boolean isFail = false;

  public DbSqliteSource(String dir) {
    dbPath = dir;
  }

  public void init() {
    try {
      Class.forName(driver);
    } catch (Exception e) {
      e.printStackTrace();
      isFail = true;
      logger.error("SDD-SOURCE-> 驱动加载失败[{}]!", driver);
      return;
    }
    if (AC.notEmpty(url)) {
      if (url.startsWith("@@")) {
        dbPath = AC.system.getClassPath() + "../../dbs/" + M.subI(url, 2);
      } else {
        dbPath = url;
      }
    }
    if (AC.empty(dbPath)) {
      dbPath = AC.system.getClassPath() + "../../sqlite3";
    }
    File f = new File(dbPath);
    if (!f.exists()) {
      f.mkdirs();
    }
    for (int i = 0; i < maxCount; i++) {
      links.add(new SddDataSourceConnection(this, null));
    }
    logger.info("SDD-SOURCE-> init ok!");
    timerFree = new Timer();
    timerFree.schedule(new TimerTask() {
      @Override
      public void run() {
        keepMinLink(); // 维持最小链接
        checkFreeConn(); // 关闭废弃链接
      }
    }, 500, timerFreePeriod);
  }

  // 关闭废弃链接
  void checkFreeConn() {
    long nowMs = System.currentTimeMillis();
    int aliveCount = 0;
    int closeFree = 0;
    for (SddDataSourceConnection scon : links) {
      if (scon.isClose() || scon.isTimeOut(nowMs, maxWaitMs)) {
        scon.destroy();
      } else {
        aliveCount++;
        if (aliveCount > minCount && scon.isTimeOut(nowMs, peakWaitMs)) {
          scon.destroy();
          closeFree++;
        }
      }
    }
    if (logPoolInfo)
      logger.info("SDD-SOURCE-> closeFree: {}", closeFree);
  }

  // 维持最小链接
  void keepMinLink() {
    int aliveCount = 0;
    for (SddDataSourceConnection scon : links) {
      if (!scon.isClose()) {
        aliveCount++;
      }
    }

    if (aliveCount < minCount) {
      for (SddDataSourceConnection scon : links) {
        if (scon.isClose()) {
          Connection conn = newConn();
          if (conn == null) {
            break;
          }
          scon.setConn(conn);
          aliveCount++;
          if (aliveCount >= minCount) {
            break;
          }
        }
      }
    }
  }

  // 创建链接
  public Connection newConn() {
    if (isFail) {
      return null;
    }
    try {
      return DriverManager.getConnection("jdbc:sqlite:" + dbPath + File.separator + "slite.db");
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    return null;
  }

  public SddDataSourceConnection getConnection() {
    if (isFail) {
      return null;
    }
    // int i = 0;
    for (SddDataSourceConnection scon : links) {
      if (!scon.isClose() && !scon.isUse()) {
        scon.setUse(true);
        return scon;
      }
      // else {
      // logger.info("{} - close: {}, use: {}", i, scon.isClose(), scon.isUse());
      // }
      // i++;
    }
    // 没有空闲-创建一个
    for (SddDataSourceConnection scon : links) {
      if (scon.isClose() && !scon.isUse()) {
        scon.setUse(true);
        Connection conn = newConn();
        if (conn == null) {
          break;
        }
        scon.setConn(conn);
        return scon;
      }
    }
    return null;
  }
}
